<?php

namespace App\Services\Utils;

use App\Services\AliYun\OSS;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;

trait OSSable
{
    protected $default_key_prefix = 'misc/';

    /**
     * @param UploadedFile $file
     * @param string $key_prefix - 文件夹路径
     * @param bool $need_hash - 是否使用 hash
     * @param string $custom_ext - 自定义扩展后缀名
     *
     * @return string
     * @throws \Exception
     */
    public function uploadPostFile(UploadedFile $file,
                                   string $key_prefix = '',
                                   bool $need_hash = true,
                                   string $custom_ext = ''): string
    {
        $file_origin_name = $file->getClientOriginalName();
        $dir_name = $this->normalizeOSSDirName($key_prefix);

        if ($need_hash) {
            $file_name = $this->hashTimeFileName($file_origin_name, $custom_ext);
        } else {
            $file_name = $file_origin_name;
        }

        $object_key = $dir_name . $file_name;

        OSS::getClient()->putObject(
            OSS::projectBucket(),
            $object_key,
            $file->get()
        );

        return $object_key;
    }

    /**
     *  保存指定名称
     *
     * @param UploadedFile $file
     * @param string $object_key_prefix
     * @param $file_name
     *
     * @return string
     * @throws \Exception
     */
    public function uploadPostFileWithName(UploadedFile $file, $object_key_prefix = '', $file_name = '')
    {
        $dir_name = $this->normalizeOSSDirName($object_key_prefix);

        $key = $dir_name . $file_name;

        OSS::getClient()->putObject(OSS::projectBucket(), $key, $file->get());

        return $key;
    }


    /**
     * @param string $file_path
     * @param string $key_prefix
     * @param bool $hash_key
     *
     * @return string
     * @throws \OSS\Core\OssException
     */
    public function uploadFile($file_path, $key_prefix = '', $hash_key = true)
    {
        $prefix = $this->normalizeOSSDirName($key_prefix);

        if ($hash_key) {
            $file_name = $this->hashTimeFileName($file_path);
        } else {
            $file_name = pathinfo($file_path, PATHINFO_BASENAME);
        }

        $key = $prefix . $file_name;

        OSS::getClient()->putObject(OSS::projectBucket(), $key, file_get_contents($file_path));

        return $key;
    }

    /**
     * @param $key
     *
     * @return string
     * @throws \OSS\Core\OssException
     */
    public function getStaticUrl(string $key)
    {
        return head(explode('?', $this->getOSSUrl($key)));
    }

    /**
     * @param $dir_name
     * @return string
     */
    private function normalizeOSSDirName($dir_name = null)
    {
        return $dir_name
            ? Str::finish($dir_name, '/')
            : $this->default_key_prefix;
    }

    /**
     * @param $objKey
     *
     * @return mixed
     * @throws \OSS\Core\OssException
     */
    public function getOSSUrl(string $objKey): string
    {
        if (is_int(strpos($objKey,'http'))) {
            return $objKey;
        }

        return OSS::getClient()->signUrl(OSS::projectBucket(), $objKey);
    }

    /**
     * Delete
     */

    /**
     * @param array $column_names
     * @throws \Exception
     */
    public function deleteOSSColumns(array $column_names)
    {
        if (! is_array($column_names)) {
            throw new \Exception('column_names need to be an array');
        }

        array_map([$this, 'deleteOSSColumn'], $column_names);
    }
    /**
     * @param array $column_names
     * @throws \Exception
     */
    public function deleteOSSJsonColumns(array $column_names)
    {
        if (! is_array($column_names)) {
            throw new \Exception('column_names need to be an array');
        }

        array_map([$this, 'deleteOSSJsonArr'], $column_names);
    }
    /**
     * @param string $column_name
     * @return bool|null|string
     * @throws \Exception
     */
    public function deleteOSSColumn($column_name)
    {
        if (! $column_name || ! is_string($column_name)) {
            throw new \Exception('column_name need to be an column name');
        }

        if (! isset($this->attributes) || ! $this->attributes[$column_name]) return null;

        return $this->deleteOSSKey($this->attributes[$column_name]);
    }
    /**
     * @param $key
     * @return bool
     */
    public function deleteOSSKey($key): bool
    {
        if (! $this->isOSSKeyValidated($key)) {
            return false;
        }

        try {
            OSS::getClient()->deleteObject(OSS::projectBucket(), $key);

            return true;
        } catch (\Exception $e) {
            logger("[OSS ERROR] OSS key: {$key} delete error: {$e->getMessage()}");

            return false;
        }
    }
    /**
     * @param $column_name
     * @return bool|null
     * @throws \Exception
     */
    public function deleteOSSJsonArr($column_name)
    {
        if ((! $column_name) || (! is_string($column_name))) {
            throw new \Exception('column_name need to be an column name');
        }

        if (empty($this->attributes[$column_name])) {
            return null;
        }

        $key_arr = json_decode($this->attributes[$column_name]);

        if (! is_array($key_arr) || ! count($key_arr)) {
            return false;
        } else {
            array_map([$this, 'deleteOSSKey'], $key_arr);

            return true;
        }
    }
    /**
     * @param mixed $key
     * @return bool
     */
    private function isOSSKeyValidated($key)
    {
        return $key && is_string($key) && mb_strpos($key, 'http') !== 0;
    }

    /**
     * Migration Deprecated
     */

    /**
     * @deprecated
     *
     * @param string $bos_key
     *
     * @return string
     * @throws \OSS\Core\OssException
     */
    public function getBosUrl($bos_key)
    {
        return $this->getOSSUrl($bos_key);
    }
    /**
     * @deprecated
     *
     * @param array $column_names
     * @throws \Exception
     */
    public function deleteKeyColumns(array $column_names)
    {
        $this->deleteOSSColumns($column_names);
    }
    /**
     * @deprecated
     *
     * @param array $column_names
     * @throws \Exception
     */
    public function deleteBosJsonColumns(array $column_names)
    {
        if (! is_array($column_names)) {
            throw new \Exception('column_names need to be an array');
        }

        array_map([$this, 'deleteBosJsonArr'], $column_names);
    }
    /**
     * @deprecated
     *
     * @param string $column_name
     * @return bool|null|string
     * @throws \Exception
     */
    public function deleteBosColumn($column_name)
    {
        return $this->deleteOSSColumn($column_name);
    }

    /**
     * @deprecated
     *
     * @param $key
     *
     * @return bool
     */
    public function deleteKey($key)
    {
        return $this->deleteOSSKey($key);
    }

    /**
     * @deprecated
     *
     * @param $column_name
     * @return bool|null
     * @throws \Exception
     */
    public function deleteBosJsonArr($column_name)
    {
        return $this->deleteOSSJsonArr($column_name);
    }

    /**
     * @param string $attribute
     *
     * @return string|null
     * @throws \OSS\Core\OssException
     */
    protected function attributeToStaticUrl(string $attribute): ?string
    {
        /** @var Model|OSSable $this */
        return empty($this->attributes[$attribute])
            ? null
            : $this->getStaticUrl($this->attributes[$attribute]);
    }

    /**
     * @param string|null $file_path - 文件路径
     * @param string $custom_ext - 自定义后缀
     *
     * @return string
     */
    private function hashTimeFileName(string $file_path,
                                      string $custom_ext = ''): string
    {
        $hash = md5($file_path . time());
        $time_stamp = date('_Y_m_d');
        $ext = $custom_ext ?: pathinfo($file_path, PATHINFO_EXTENSION);

        return $hash . $time_stamp . '.' . $ext;
    }
}
